07 de Junio, 2017

Contenidos

  1. Introducción
  2. Motivación
  3. Empezando a caminar con Shiny
  4. Componentes básicas de una aplicación Shiny
    • User interface (ui)
      • Layouts
      • Widgets: Inputs
    • Server
      • Widgets: Outputs
      • Resctividad

1. Introducción

1. Introducción

Hasta el momento hemos hablado de la creación de documentos mediante R Markdown, interactivos o no, pensando en un uso local.

Estamos en la sociedad de la información e internet lo domina todo. ¿Podemos utilizar internet para ayudar en la investigación reproducible?

  • Shiny es un paquete de R que nos permite desarrollar aplicaciones web interactivas.
    • Esta siendo desarrollado por los creadores de RStudio, destacan especialmente Winston Chang, Joe Cheng, JJ Allaire, Yihui Xie, Jonathan McPherson, …
    • No necesita conocimientos de lenguajes de programación como HTML, CSS o JavaScript.

2. Motivación

Ejemplos

El papel de Shiny en la Investigación Reproducible

Shiny es una herramienta perfecta para compartir nuestros estudios, permitiendo el acceso a la información. Esto genera numerosos beneficios:

  • Mayor visibilidad de nuestro trabajo (mayor control)
  • Transparencia
  • Extensión a artículos científicos (apéndices más actuales)
    • Cada vez más revistas piden acceso al código y los datos
  • Complemento a paquetes de R
  • Objetivo: Aprender a crear aplicaciones web utilizando Shiny.

3. Empezando a caminar con Shiny

Instalación de Shiny y creación de la primera app

Paso 1. Abrimos RStudio.

Paso 2. Instalamos el paquete "shiny" desde CRAN (en caso de no haberlo instalado previamente):

install.packages("shiny", dependencies = TRUE)

Instalación de Shiny y creación de la primera app

Paso 3. Seleccionamos File >> New File >> Shiny Web App….

Instalación de Shiny y creación de la primera app

Paso 4. Indicamos el nombre de la aplicación, el número de archivos que queremos y el directorio de creación.

  1. Single File: Se crea una carpeta con el nombre de la aplicación en el directorio indicado. Esta carpeta contendrá el archivo app.R.
  2. Multiple File: Se crea una carpeta con el nombre de la aplicación en el directorio indicado. Esta carpeta contendrá los archivos ui.R y server.R.

Ejemplo Shiny >> Single File (app.R)

Ejemplo Shiny >> Multiple File (ui.R/server.R)


Ejemplo Shiny >> Cerrar la app

Si no cerramos la app, esta se queda ejecutándose en R y no podremos seguir trabajando.

4. Componentes básicas de una aplicación Shiny

4. Componentes básicas de una aplicación Shiny

Para crear una aplicación Shiny es suficiente con tener los scripts ui.R y server.R (app.R) dentro de la misma carpeta. El nombre de estos scripts no se puede cambiar.

La estructura es la siguiente:

  • Carperta nombre_app:
    • ui.R y server.R (app.R)


  • Podemos añadir más archivos (.R, .RData, .jpg, .css, …) que serán ejecutados desde ui.R o server.R.

User interface (ui / ui.R)

ui / ui.R (User interface)

  • La construcción de la interfaz de usuario es como montar un puzzle.


1. Estructura general (layouts de página)

2. División de la página (layouts)

3. Contenido de la página (widgets)

User interface

Layouts

Layout para página: fluidPage() / bootstrapPage()

ui <- fluidPage(
  
  # Título de la aplicación
  titlePanel("Shiny - fluidPage")
  
)

ui <- bootstrapPage(
  
  # Título de la aplicación
  titlePanel("Shiny - bootstrapPage")
  
)

# Definición del server
server <- function(input, output) {}

# App completa con los componentes ui y server
shinyApp(ui, server)

Layouts

sidebarLayout()

verticalLayout()

splitLayout()

flowLayout()

Layout: Notas

Los elementos de la página se definen de arriba abajo.

La separación entre los elementos es con comas.

Gran flexibilidad en la configuración del IU.

Los layouts se pueden combinar unos dentro de otros, anidados.

Layout: sidebarLayout()

ui <- fluidPage(
  
  titlePanel("Shiny - sidebarLayout"),
  
  sidebarLayout(
    
    # Barra lateral con un selector deslizante
    sidebarPanel(
      sliderInput("obs",
                  "Número de observaciones:",
                  min = 0, max = 1000,
                  value = 500)
    ),
    
    mainPanel(
      plotOutput("distPlot")
    )
  )
)

server <- function(input, output) {
  output$distPlot <- renderPlot({
    hist(rnorm(input$obs))
  })
}

shinyApp(ui, server)

Layout: verticalLayout()

ui <- fluidPage(
  titlePanel("Shiny - verticalLayout"),
  
  verticalLayout(
    a(href="http://example.com/link1", "Link Primero"),
    a(href="http://example.com/link2", "Link Segundo"),
    a(href="http://example.com/link3", "Link Tercero")
  )
)

server <- function(input, output) {}

shinyApp(ui, server)

Layout: splitLayout()

ui <- fluidPage(
  titlePanel("Shiny - splitLayout"),
  
  splitLayout(
    plotOutput("plot1"),
    plotOutput("plot2")
  )
)

server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}

shinyApp(ui, server)

Layout: conditionalPanel()

ui <- fluidPage(
  titlePanel("Shiny - conditionalPanel"),
  
  sidebarPanel(
   selectInput("plotType", "Plot Type",
      c(Scatter = "scatter", Histogram = "hist")),

   conditionalPanel(
      condition = "input.plotType == 'hist'",
      selectInput("breaks", "Breaks",
         c("Sturges", "[Custom]" = "custom")),
      conditionalPanel(
         condition = "input.breaks == 'custom'",
         sliderInput("breakCount", "Break Count", min = 1, max = 1000, value = 10)
      )
   )
  )
)

server <- function(input, output) {}

shinyApp(ui, server)

Si condicionamos por una variable definida por el usuario:
- Nos referiremos a la variable como input.xxx, NO usaremos input$xxx.

Layer tabPanels: tabsetPanel()

ui <- fluidPage(
  titlePanel("Shiny - tabsetPanel"),
  
  mainPanel(
    tabsetPanel(
      tabPanel("Plot 1", plotOutput("plot1")),
      tabPanel("Plot 2", plotOutput("plot2"))
    )
  )
)

server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}

shinyApp(ui, server)

Layer tabPanels: navlistPanel()

ui <- fluidPage(
  titlePanel("Shiny - navlistPanel"),
  
  navlistPanel("PANELES",
    tabPanel("Primer Panel",
             plotOutput("plot1")),
    tabPanel("Segundo Panel",
             plotOutput("plot2"))
  )
)

server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}

shinyApp(ui, server)

Layer tabPanels: navbarPage()

ui <- navbarPage("Shiny - navbarPage",
  tabPanel("Primera Página",
           plotOutput("plot1")),
  tabPanel("Segunda Página",
           plotOutput("plot2"))
)

server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}

shinyApp(ui, server)

User interface

Widgets: Inputs

Widgets: Inputs

Control widgets

Server

server / server.R

¿Qué hacemos en el server.R?

Al referirnos a variables introducidas por el usuario mediante widgets definidos en el ui, emplearemos input$xxx, siendo xxx es el nombre indicado en el argumento InputID del widget correspondiente.

En el server es donde debemos realizaremos los cálculos y gráficos que queramos mostrar en el ui.

  • Cada acción o grupo de acciones relacionadas, debe ser definida en una función.
  • Los elementos del server no tienen que seguir un orden de aparición (a diferencia de los del ui).

Server

Widgets: Outputs

Widgets: Outputs

Server

Reactividad

Utilización de expresiones reactivas

  • Shiny responde de forma inmediata a los cambios introducidos por el usuario pero, ¿siempre queremos esto?

  • En ocasiones nos interesará realizar un cálculo o un gráfico que dependa de más de una variable, ¿que sucedería si permitimos que Shiny nos de una respuesta instantáneamente?

Funciones reactivas

render*() -> Salida por pantalla

  • Las funciones render*() crean una salida para mostrar por pantalla.

  • Los resultados de estas funciones siempre se guardan en output$.

output$hist <- renderPlot({
  hist(rnorm(input$N))
})

reactive() -> Modularizar código

  • Las expresiones reactive() generan un objeto para ser utilizado. Este objeto cambiará de valor cada vez que se modifique algún input$ de su interior.

  • El objeto generado se llama como una función.

data <- reactive({
    rnorm(input$N)
})

output$hist <- renderPlot({
  hist(data())
})
  • Las funciones reactive nos permiten una programación modular. Esto ayuda a que la ejecución de las apps sea más rápida y fluida

isolate() -> Previene reacciones

  • isolate() hace que un objeto no sea reactivo.
data <- reactive({
    rnorm(input$N)
})

output$hist <- renderPlot({
  hist(data(),
       main = isolate(input$titulo))
})

observeEvent() -> Activación de código

  • Activa el código para que se ejecute en el servidor.
ui <- fluidPage(
  actionButton(inputId = "accion",
    label = "Acción")
)
server <- function(input, output) {
  observeEvent(input$accion, {
    print(runif(1, 1, 100))
})
}

shinyApp(ui = ui, server = server)

observe()

  • Se comporta como el observeEvent() pero reaccionando a todos los valores reactivos que contiene.

eventReactive() -> Retrasar reacciones

  • Expresión reactiva que solo responden a un valor específico.
ui <- fluidPage(
  numericInput(inputId = "N", label = "Generar número aleatorio entre 0 y:", value = 100, min = 0, max = 300),
  actionButton(inputId = "actualizar",
    label = "Actualizar"),
  textOutput("rnd")
)
server <- function(input, output) {
  data <- eventReactive(input$actualizar, {
    runif(1, 1, input$N)
  })
  
  output$rnd <- renderText({data()})
}

shinyApp(ui = ui, server = server)
  • Podemos emplear eventReactive() para retrasar la ejecución de determinadas reacciones.

reactiveValues()

  • reactiveValues() crea una lista de valores reactivos.

-Estos valores reactivos pueden manejarse (usualmente mediante observeEvent())

ui <- fluidPage(
  actionButton(inputId = "norm", label = "Normal"),
  actionButton(inputId = "unif", label = "Uniforme"),
  plotOutput("hist")
)
server <- function(input, output) {
  rv <- reactiveValues(data = rnorm(100))
  observeEvent(input$norm, { rv$data <- rnorm(100) })
  observeEvent(input$unif, { rv$data <- runif(100) })
  output$hist <- renderPlot({
    hist(rv$data)
}) }
shinyApp(ui = ui, server = server)

Bibliografía y recursos